from os import path
import logging
from datetime import datetime

import colander
import deform
from deform import widget
from deform import ValidationFailure
from deform.template import ZPTRendererFactory
from pkg_resources import resource_filename
from pyramid.httpexceptions import HTTPFound
from pyramid.renderers import render, get_renderer
from pyramid.security import authenticated_userid
from pyramid.security import remember
from pyramid.security import forget
from pyramid.url import resource_url
from webob import Response

from ampersands.core import vocabulary, schemas


log = logging.getLogger('ampersands.core')


default_dir = resource_filename('ampersands.core', 'templates/widgets/')
default_renderer = ZPTRendererFactory((default_dir,))


def user_metadata(request):
    return request.get('repoze.who.identity', {}).get('properties', {})


##
## Add Collection
##

def add_collection(context, request):
    # initialise new collection
    collection = context.new(authenticated_userid(request))
    return HTTPFound(location=resource_url(collection, request, 'edit.html'))


##
## Split Form Views
##
class Collection(object):
    schemas = {"basic": schemas.Basic(),
               "rights": schemas.cc_license_form,
               "location": schemas.Locations(),
               "data": schemas.DataSources(),
               "activities": schemas.Activities(),
               "parties": schemas.Parties(),
               }
    form_order = ["basic", "rights", "data",
                  "location", "activities", "parties"]

    def __init__(self, context, request):
        self.context = context
        self.request = request
        self.logged_in = authenticated_userid(request)


class EditCollection(Collection):

    def getForm(self, formid):
        schema = self.schemas[formid]
        # init the schema if it's dynamic
        if callable(schema):
            schema = schema(self.context, self.request)

        options = """
            {success:
               function (rText, sText, xhr, form) {
                  deform.load(); //initialise any callbacks
                  cb_nextForm(rText, sText, xhr, form);
               },
            """

        # if last form don't show the next button
        if self.form_order.index(formid) == 0:
            buttons = ('Next',)
            options = options + "}"
        elif self.form_order.index(formid) + 1 < len(self.form_order):
            buttons = ("Previous", 'Next',)
            options = options + "}"
        else:
            buttons = ("Previous", 'Register',)
            options = options + """
                    replaceTarget: false,
                    }
                    """

        form = deform.Form(schema, renderer=default_renderer,
                           use_ajax=True, buttons=buttons,
                           formid=formid, ajax_options=options)
        return form

    def nextForm(self, formid=None):
        """get the next form
        """
        if not formid:
            formid = self.context.getForm()
        newindex = self.form_order.index(formid) + 1
        if newindex < len(self.form_order):
            self.context.setForm(self.form_order[newindex])
            return self.form_order[newindex]
        return formid

    def previousForm(self, formid=None):
        """get the previous form
        """
        if not formid:
            formid = self.context.getForm()
        newindex = self.form_order.index(formid) - 1
        if newindex >= 0 and newindex < len(self.form_order):
            self.context.setForm(self.form_order[newindex])
            return self.form_order[newindex]
        return formid

    def getFormList(self):
        for form in self.form_order:
            css_style = []
            if form == self.current_form:
                css_style.append("selected")
            if form in self.context.listForms():
                css_style.append("valid")

            yield (form, " ".join(css_style), "./edit.html?formid=%s" % form)

    def __call__(self):
        if self.request.POST:
            controls = self.request.POST.items()
            formid = self.request.POST["__formid__"]
            form = self.getForm(formid)
            try:
                captured = form.validate(controls)
                html = form.render(captured)
            except ValidationFailure, e:
                # forms are exempt from validation when changing
                # form contents
                if self.request.GET.get("reloadform", "") == "true":
                    # reinitialise a new form because there is a cache
                    form = self.getForm(formid)
                    html = form.render()
                else:
                    html = e.render()

            else:
                self.context.saveForm(formid, captured)
                if "Next" in self.request.POST:
                    newformid = self.nextForm(formid)
                elif "Previous" in self.request.POST:
                    newformid = self.previousForm(formid)
                elif "Register" in self.request.POST:
                    for form in self.form_order:
                        if form not in self.context.listForms():
                            return HTTPFound(location=resource_url(self.context,
                                                self.request,
                                                "edit.html",
                                                query={"formid": form,
                                                       "validate": "true"}))

                    return HTTPFound(location=resource_url(self.context,
                                                           self.request,
                                                           'register.html'))
                else:
                    newformid = formid
                html = self.getForm(newformid).render(
                    self.context.getData(newformid))

        else:
            if 'formid' in self.request.GET:
                form = self.getForm(self.request.GET['formid'])
            else:
                form = self.getForm(self.context.getForm())
            formid = self.request.GET.get('formid', self.context.getForm())
            html = form.render(self.context.getData(formid))

        if self.request.is_xhr:
            return Response(html)
        self.current_form = form.formid

        main = get_renderer('templates/master.pt').implementation()
        return {'project': 'ampersands', 'form': html,
                'title': 'Edit Collection',
                'form_list': self.getFormList(),
                "main": main}


##
## Autocomplete Subject List
##

def autocomplete_subject(context, request):
    text = request.params.get('term', '')
    return [x for x in vocabulary.for2008()
            if text in x['label'].lower()]


##
## View Collection
##

class ViewCollection(Collection):
    sidebar_template = "templates/sidebar.pt"
    schema = schemas.Basic()
    schemas = {"basic": schemas.Basic(),
               "rights": schemas.ReadonlyLicense(),
               "data": schemas.DataSources(),
               "location": schemas.Locations(),
               "activities": schemas.Activities(),
               "parties": schemas.Parties(),
               }

    def renderForms(self):
        html = ""
        for formid in self.form_order:
            if formid == "rights":
                data = schemas.render_license(self.context.getData(formid))
            else:
                data = self.context.getData(formid)

            schema = self.schemas[formid]
            # init the schema if it's dynamic
            if callable(schema):
                schema = schema(self.context, self.request)

            form = deform.Form(schema, renderer=default_renderer)
            form_output = form.render(data, readonly=True)
            html = html + form_output
        return html

    def __call__(self):
        """show the addform for adding a new user"""
        form_output = self.renderForms()
        main = get_renderer('templates/master.pt').implementation()
        return {'project': 'ampersands',
                'title': 'View Collection',
                'main': main, 'form': form_output}

    def getSidebar(self):
        items = []
        items.append(("edit", "./edit.html"))
        items.append(("delete", "./delete.html"))
        sidebar = render(self.sidebar_template, {"items": items})
        return sidebar


class RegisterCollection(Collection):
    schema = schemas.Basic()
    schemas = {"basic": schemas.Basic(),
               "rights": schemas.ReadonlyLicense(),
               "data": schemas.DataSources(),
               "location": schemas.Locations(),
               "activities": schemas.Activities(),
               "parties": schemas.Parties(),
               }

    def renderForms(self):
        html = ""
        for formid in self.form_order:
            if formid == "rights":
                data = schemas.render_license(self.context.getData(formid),
                            user=user_metadata(self.request).get('display'),
                            title=self.context.getTitle())
            else:
                data = self.context.getData(formid)

            schema = self.schemas[formid]
            # init the schema if it's dynamic
            if callable(schema):
                schema = schema(self.context, self.request)

            form = deform.Form(schema, renderer=default_renderer)
            form_output = form.render(data, readonly=True)
            html = html + form_output
        return html

    def __call__(self):
        """show the addform for adding a new user"""
        if self.request.POST:
            if "Cancel" in self.request.POST:
                return HTTPFound(location=resource_url(self.context.__parent__,
                                                       self.request))
            elif "Edit" in self.request.POST:
                return HTTPFound(location=resource_url(self.context,
                                                       self.request,
                                                       "edit.html"))
            elif "Publish" in self.request.POST:
                self.context.register()
                r = RIFCSCollection(self.context, self.request)
                xml = r.render()
                repo = self.request.registry.settings['rifcs_repository']
                f = open(path.join(repo, self.context.__name__ + ".xml"), "w")
                f.write(xml)
                f.close()
                return HTTPFound(location=resource_url(self.context.__parent__,
                                                       self.request))

        form_output = self.renderForms()
        main = get_renderer('templates/master.pt').implementation()
        return {'project': 'ampersands',
                'title': 'Register Collection',
                'now': datetime.now().strftime("%d/%m/%Y"),
                'user': user_metadata(self.request),
                'main': main, 'form': form_output}


def delete_collection(context, request):
    if request.POST:
        if "Cancel" in request.POST:
            return HTTPFound(location=resource_url(context,
                                                   request))
        elif "Delete" in request.POST:
            context.delete()
            return HTTPFound(location=resource_url(context.__parent__,
                                                   request))
    schema = colander.SchemaNode(colander.Mapping())
    schema.add(colander.SchemaNode(colander.String(),
                                   widget=widget.TextInputWidget(
                                       template="readonly/textinput"),
                                   missing="",
                                   default="Are you sure you? Once you delete"
                                   " this entry you will be unable to get it"
                                   " back."))
    form = deform.Form(schema, renderer=default_renderer,
                       buttons=("Delete", "Cancel"))
    form_output = form.render()
    main = get_renderer('templates/master.pt').implementation()
    return {'project': 'ampersands',
            'title': 'Delete Collection',
            'main': main, 'form': form_output,
            }


class list_collections(object):
    def __init__(self, context, request):
        self.context = context
        self.request = request
        self.logged_in = authenticated_userid(request)

    def __call__(self):
        data = []
        for collection in self.context.getCollections(self.request):
            data.append({'name': collection.getTitle(),
                         'url': resource_url(collection, self.request)})
        main = get_renderer('templates/master.pt').implementation()
        return {'project': 'ampersands',
                'context': self.context,
                "main": main,
                'data': data}


def rifcs(context, request):
    log.info(context.__dict__)
    log.info(dir(context))
    log.info(context.values())
    collections = schemas.Collection()
    schema = colander.SchemaNode(colander.Mapping())
    schema.add(colander.SchemaNode(colander.Sequence(),
                                   collections, name='collections'))

    data = schema.serialize({'collections': context.values()})
    xml = render('templates/rifcs.pt', data=data)
    return Response(xml, content_type='text/xml')


class RIFCSCollection(Collection):
    schema = schemas.Basic()
    schemas = {"basic": schemas.Basic(),
               "rights": schemas.ReadonlyLicense(),
               "data": schemas.DataSources(),
               "location": schemas.Locations(),
               "activities": schemas.Activities(),
               "parties": schemas.Parties(),
               }

    def renderSchemas(self):
        rdata = {}
        for formid in self.form_order:
            if formid == "rights":
                data = schemas.render_license(self.context.getData(formid),
                            user=user_metadata(self.request).get('display'),
                            title=self.context.getTitle())
            else:
                data = self.context.getData(formid)

            schema = self.schemas[formid]
            # init the schema if it's dynamic
            if callable(schema):
                schema = schema(self.context, self.request)
            rdata[formid] = schema.serialize(data)
        return rdata

    def render(self):
        data = self.renderSchemas()
        data['identifier'] = self.context.data["identifier"]
        data['dateAccessioned'] = self.context.data['dateAccessioned']
        data['dateModified'] = self.context.data['dateModified']
        xml = render('templates/rifcs.pt', {"data":
                                            {"collections": [data]}})
        return xml

    def __call__(self):
        xml = self.render()
        return Response(xml, content_type='text/xml')


def index(request):
    log.info('test view')
    main = get_renderer('templates/master.pt').implementation()
    return {'project': 'ampersands', "main": main}


def login(request):
    login_url = resource_url(request.context, request, 'login')
    referrer = request.referer
    if referrer == login_url:
        referrer = '/'  # never use the login form itself as came_from
    came_from = request.GET.get('came_from', referrer)
    logged_in = authenticated_userid(request)
    if logged_in:
        return HTTPFound(location=came_from)

    schema = colander.SchemaNode(colander.Mapping())
    schema.add(colander.SchemaNode(colander.String(),
                                   name="login",
                                   title="User Name",
                                   missing=""))
    schema.add(colander.SchemaNode(colander.String(),
                                   widget=widget.TextInputWidget(
                                       template="password"),
                                   name="password",
                                   title="Password",
                                   missing=""))
    form = deform.Form(schema, renderer=default_renderer,
                       action="/dologin",
                       buttons=("Log In", "Cancel"))

    if request.referrer.startswith(login_url):
        form.error = colander.Invalid(schema,
                                      msg="Invalid user name or password")
        form.children[0].error = colander.Invalid(schema,
                                      msg="Invalid user name or password")
    form_output = form.render()

    main = get_renderer('templates/master.pt').implementation()
    return {"project": 'ampersands',
            "title": 'Login',
            "main": main,
            "url": request.application_url + '/login',
            "came_from": came_from,
            'form': form_output,
            }


def logout(request):
    r = Response('forbidden')
    r.status = 401
    return r


def forbidden_view(request):
    r = Response('forbidden')
    r.status = 401
    return r
